Skip to content

Feat/template import export#562

Open
Shubh-Raj wants to merge 3 commits intoaccordproject:mainfrom
Shubh-Raj:feat/template-import-export
Open

Feat/template import export#562
Shubh-Raj wants to merge 3 commits intoaccordproject:mainfrom
Shubh-Raj:feat/template-import-export

Conversation

@Shubh-Raj
Copy link
Contributor

@Shubh-Raj Shubh-Raj commented Jan 13, 2026

(Update description)

Closes #6

This PR adds the ability to export templates to .cta (Contract Template Archive) files and import them back, creating a complete round-trip workflow for saving and restoring template work.

Approach

The archive utility mirrors the CTA file structure used by cicero-core's TemplateSaver / TemplateLoader, using JSZip directly since cicero-core does not currently have a browser-compatible bundle (it depends on Node.js fs module).

Changes

  • Add jszip dependency for client-side ZIP archive handling
  • Add src/utils/archive/archive.ts with createArchive, extractArchive, and downloadBlob utilities
  • Add exportTemplate and importTemplate store actions with error handling
  • Add Export button (FiDownload icon) and Import button (FiUpload icon) to PlaygroundSidebar
  • File input restricted to .cta files only
  • Success/error toast messages on import
  • importTemplate clears agreementHtml to avoid stale content flash and rethrows for caller error handling
  • Add unit tests for archive utilities (round-trip, name extraction, error cases)
  • Add store action tests for exportTemplate and importTemplate
  • Update PlaygroundSidebar tests for new Export/Import buttons

Flags

  • Uses JSZip library for client-side archive creation/extraction
  • CTA format follows Accord Project structure: model/model.cto, text/grammar.tem.md, text/sample.md, package.json
  • The

Screenshots or Video

Screencast.from.2026-01-13.15-59-37.webm

Related Issues

Author Checklist

  • Ensure you provide a DCO sign-off for your commits using the --signoff option of git commit.
  • Vital features and changes captured in unit and/or integration tests
  • Commits messages follow AP format
  • Extend the documentation, if necessary
  • Merging to main from fork:branchname

@Shubh-Raj Shubh-Raj requested a review from a team as a code owner January 13, 2026 10:25
@netlify
Copy link

netlify bot commented Jan 13, 2026

Deploy Preview for ap-template-playground ready!

Name Link
🔨 Latest commit 81af571
🔍 Latest deploy log https://app.netlify.com/projects/ap-template-playground/deploys/69af276a727de30008966da4
😎 Deploy Preview https://deploy-preview-562--ap-template-playground.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Shubh-Raj
Copy link
Contributor Author

Hi @dselman @mttrbrts @martinhalford — I completed this assigned issue a while back and wanted to follow up for a review whenever you’re available. Thank you!

@Shubh-Raj
Copy link
Contributor Author

Resolved the merge conflicts.

@Shubh-Raj
Copy link
Contributor Author

Following the recent meeting discussion in wg call, I would like to have more insights on the implicit/local state management. In my PR, importTemplate directly updates the store, but I’d love guidance on what to improve in this.

Happy to refactor my implementation accordingly.

Copilot AI review requested due to automatic review settings February 24, 2026 14:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements a complete export/import workflow for templates using the .cta (Contract Template Archive) format, addressing Issue #6 which requested a way to save templates since there's currently no save functionality in the playground.

Changes:

  • Added JSZip-based archive utilities for creating and extracting CTA files with model, template, and data
  • Integrated export/import functionality into the Zustand store with proper error handling
  • Added Export and Import buttons to the PlaygroundSidebar UI with file picker integration

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/utils/archive/archive.ts New utility module providing createArchive, extractArchive, and downloadBlob functions for CTA file handling
src/tests/utils/archive.test.ts Unit tests for archive utilities including round-trip verification
src/store/store.ts Added exportTemplate and importTemplate actions to store with error handling and state management
src/components/PlaygroundSidebar.tsx Added Export/Import buttons with file input integration and handler functions
src/tests/components/PlaygroundSidebar.test.tsx Updated tests to verify Export/Import buttons render correctly and mocked store functions
package.json Added jszip dependency (v3.10.1) for ZIP file operations
README.md Updated feature list to mention export/import capability

Comment on lines +332 to +367
exportTemplate: async () => {
const { sampleName, modelCto, templateMarkdown, data } = get();
try {
const blob = await createArchive(sampleName, modelCto, templateMarkdown, data);
const filename = `${sampleName.toLowerCase().replace(/\s+/g, '-')}.cta`;
downloadBlob(blob, filename);
} catch (error) {
console.error('Export failed:', error);
set(() => ({
error: 'Failed to export template: ' + (error instanceof Error ? error.message : 'Unknown error'),
isProblemPanelVisible: true,
}));
}
},
importTemplate: async (file: File) => {
try {
const { name, model, template, data } = await extractArchive(file);
set(() => ({
sampleName: name,
templateMarkdown: template,
editorValue: template,
modelCto: model,
editorModelCto: model,
data: data,
editorAgreementData: data,
error: undefined,
}));
await get().rebuild();
} catch (error) {
console.error('Import failed:', error);
set(() => ({
error: 'Failed to import template: ' + (error instanceof Error ? error.message : 'Unknown error'),
isProblemPanelVisible: true,
}));
}
},
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new exportTemplate and importTemplate store actions lack unit test coverage. Following the pattern established in src/tests/store/generateSharebleLink.test.tsx, these critical actions should have tests that verify:

  1. exportTemplate creates a proper archive and triggers download
  2. importTemplate correctly updates store state with extracted data
  3. Error handling works correctly for both actions (e.g., invalid files, missing fields)

This is important because these actions handle user data and file operations, which are prone to edge cases and errors.

Copilot uses AI. Check for mistakes.
if (packageFile) {
try {
const pkg = JSON.parse(await packageFile.async('string'));
name = pkg.name || pkg.description || name;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name extraction logic prioritizes pkg.name over pkg.description, but according to the CTA creation in createArchive, the name field is a kebab-case version (e.g., "test-template") while the description field contains the human-readable name (e.g., "Template: Test Template"). This mismatch means imported templates will show kebab-case names instead of the original user-friendly names. Consider extracting the name from the description field first (after removing the "Template: " prefix), then falling back to the name field.

Suggested change
name = pkg.name || pkg.description || name;
const description = typeof pkg.description === 'string' ? pkg.description.trim() : '';
const pkgName = typeof pkg.name === 'string' ? pkg.name.trim() : '';
if (description) {
const prefix = 'Template: ';
name = description.startsWith(prefix)
? description.slice(prefix.length).trim() || name
: description;
} else if (pkgName) {
name = pkgName;
}

Copilot uses AI. Check for mistakes.
<input
ref={fileInputRef}
type="file"
accept=".cta,.zip"
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file input accepts both .cta and .zip extensions, but the feature is specifically for CTA (Contract Template Archive) files. While CTA files are ZIP files internally, accepting generic .zip files could be confusing to users who might try to import any ZIP file. Consider accepting only .cta extension for clarity, or add validation in extractArchive to verify it's a valid CTA structure before processing.

Suggested change
accept=".cta,.zip"
accept=".cta"

Copilot uses AI. Check for mistakes.
Comment on lines +160 to +165
onClick: () => void exportTemplate()
},
{
title: "Import",
icon: FiUpload,
onClick: handleImportClick
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Export and Import buttons don't provide user feedback on success. Following the pattern used in handleShare (which shows message.success('Link copied to clipboard!')), consider adding success feedback handlers in PlaygroundSidebar. For example:

  • Export: "Template exported successfully!"
  • Import: "Template imported successfully!"

This helps users confirm their actions completed as expected, especially since export happens silently (browser download) and import makes significant state changes.

Suggested change
onClick: () => void exportTemplate()
},
{
title: "Import",
icon: FiUpload,
onClick: handleImportClick
onClick: () => {
void exportTemplate();
message.success("Template exported successfully!");
}
},
{
title: "Import",
icon: FiUpload,
onClick: () => {
void handleImportClick();
message.success("Template imported successfully!");
}

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +28
accordproject: {
template: 'clause',
runtime: 'es6'
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated package.json lacks several fields that are typically present in Accord Project CTA archives, such as author, license, and potentially cicero versioning information. While the current minimal structure may work for basic import/export, consider adding these standard fields to ensure better compatibility with other Accord Project tools that may consume these archives. Reference the Accord Project CTA specification for complete field requirements.

Suggested change
accordproject: {
template: 'clause',
runtime: 'es6'
author: 'Template Playground User',
license: 'Apache-2.0',
accordproject: {
template: 'clause',
runtime: 'es6',
cicero: {
version: '*'
}

Copilot uses AI. Check for mistakes.
Comment on lines +227 to +233
<input
ref={fileInputRef}
type="file"
accept=".cta,.zip"
onChange={(e) => void handleFileChange(e)}
style={{ display: 'none' }}
/>
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding end-to-end tests for the export/import workflow. Following the pattern in e2e/template-workflow.spec.ts, tests should verify:

  1. Export button triggers a download
  2. Import button opens file picker and successfully loads a template
  3. Round-trip workflow: export a template, then import it back and verify data preservation

While the unit tests cover the utilities well, E2E tests would ensure the complete user workflow functions correctly, especially the file picker integration and browser download behavior.

Copilot uses AI. Check for mistakes.
@Shashanth-V
Copy link

Hi @Shubh-Raj ,

I’ve been studying this PR and the Template Playground architecture as part of my initial contributions. I noticed the Copilot suggestion about missing unit test coverage for exportTemplate and importTemplate.

I’d be happy to help by adding test coverage for:

  • Successful export archive generation
  • Import correctly updating store state
  • Error handling for invalid CTA files

Please let me know if this would be helpful and if I can contribute to this PR.

Thanks!

@Shubh-Raj
Copy link
Contributor Author

Hi @Shubh-Raj ,

I’ve been studying this PR and the Template Playground architecture as part of my initial contributions. I noticed the Copilot suggestion about missing unit test coverage for exportTemplate and importTemplate.

I’d be happy to help by adding test coverage for:

  • Successful export archive generation
  • Import correctly updating store state
  • Error handling for invalid CTA files

Please let me know if this would be helpful and if I can contribute to this PR.

Thanks!

Thanks for your suggestion, but I am currently working on this.

Copy link
Member

@mttrbrts mttrbrts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the existing API for creating and loading archives. You shouldn't need to manage ZIP files directly.

Here's an example from the CLI tool
https://github.com/accordproject/template-archive/blob/main/packages/cicero-cli/lib/commands.js#L110

@Shubh-Raj
Copy link
Contributor Author

Please use the existing API for creating and loading archives. You shouldn't need to manage ZIP files directly.

Here's an example from the CLI tool https://github.com/accordproject/template-archive/blob/main/packages/cicero-cli/lib/commands.js#L110

Hi @mttrbrts,
I traced the code path from the CLI example you linked: commands.js to Template.toArchive() to TemplateSaver.toArchive() and similarly Template.fromArchive() to TemplateLoader.fromArchive().

The actual archive packaging in both methods uses JSZip internally to create/extract the CTA structure (
package.json, model/*.cto, text/grammar.tem.md, text/sample.md).

Since the playground is a browser-based app and cicero-core doesn't have a browser-compatible bundle right now (it depends on Node.js fs module), I'm mirroring the same TemplateSaver/TemplateLoader logic using JSZip directly. The archive output follows the exact same CTA file structure.

I'm also addressing the Copilot review suggestions ie. fixing name extraction, restricting import to .cta only, adding success feedback, and adding tests. Will push the updates shortly.

Add jszip dependency and archive utility module that mirrors the CTA
file structure used by cicero-core's TemplateSaver/TemplateLoader:
- package.json with accordproject metadata
- model/model.cto
- text/grammar.tem.md
- text/sample.md

Signed-off-by: Shubh-Raj <shubhraj625@gmail.com>
Copilot AI review requested due to automatic review settings March 9, 2026 19:40
@Shubh-Raj Shubh-Raj force-pushed the feat/template-import-export branch from 1d6051a to df12cfe Compare March 9, 2026 19:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.

Add exportTemplate and importTemplate store actions that use the
archive utility. Add Export/Import buttons to PlaygroundSidebar with
file picker restricted to .cta files. Shows success toast on import
and error toast on failure with try/catch/finally for proper cleanup.
importTemplate rethrows errors for caller error handling and clears
agreementHtml to avoid stale content flash.

Signed-off-by: Shubh-Raj <shubhraj625@gmail.com>
Add unit tests for archive utilities and store actions:
- createArchive produces valid ZIP blob
- extractArchive reads back model, template, and data
- extractArchive throws on missing model or grammar files
- Name extraction from description field
- Full round-trip data preservation
- exportTemplate creates archive and triggers download
- importTemplate updates store state with extracted data
- Error handling for both store actions

Signed-off-by: Shubh-Raj <shubhraj625@gmail.com>
@Shubh-Raj Shubh-Raj force-pushed the feat/template-import-export branch from df12cfe to 81af571 Compare March 9, 2026 20:02
@Shubh-Raj Shubh-Raj requested a review from mttrbrts March 9, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export to "Contract Template Archive .CTA" file

4 participants